/*
 * @Author: yayalee yangcheng960813@163.com
 * @Date: 2023-11-02 19:40:21
 * @LastEditTime: 2024-07-12 11:37:14
 * @LastEditors: yayalee yangcheng960813@163.com
 * @Description: 
 * @FilePath: \unfinished-challenges\assets\core_ts\RandomUtil.ts
 * 可以输入预定的版权声明、个性签名、空行等
 */

const CHARS: string[] = [
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
];

/**
 * 可设置随机种子的随机数生成器
 * @link 文档 https://blog.csdn.net/fengqianlang/article/details/131069794
 */
export class RandomUtil {

    public static get seed() {
        return this._random.seed;
    }

    public static set seed(seed: string) {
        this.$random = RandomUtil.create(seed);
    }

    private static $random: RandomUtil;

    public static get _random(): RandomUtil {
        if (!this.$random) this.$random = RandomUtil.create();
        return this.$random;
    }

    /**
     * 随机生成一个种子编码
     * @param length 编码字节长度
     */
    public static getSeed(length: number = 8): string {
        let seed = "";
        for (let i = 0; i < length; i++) {
            seed += this.randomArray(CHARS);
        }
        return seed;
    }

    /**
     * 获取区间[0, 1)的浮点数
     * [表示大于等于；)表示小于；例如[2,3)表示大于等于2，小于3
     */
    public static random(): number {
        return this._random.random();
    }

    /**
     * 获取区间[min, max)的整数，传入1个参数则区间为[0, min)
     * [表示大于等于；)表示小于；例如[2,3)表示大于等于2，小于3
     */
    public static int(min: number, max: number = undefined!): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(this.random() * (max - min)) + min;
    }

    /**
     * 获取区间[min, max)的浮点数，传入1个参数则区间为[0, min)
     * [表示大于等于；)表示小于；例如[2,3)表示大于等于2，小于3
     */
    public static float(min: number, max: number = undefined!): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return this.random() * (max - min) + min;
    }

    /**
    * 获取区间[min, max]的整数，传入1个参数则区间为[0, min]
    * [表示大于等于；)表示小于；例如[2,3）表示大于等于2，小于3
    * @param min 
    * @param max 
    * @returns 
    */
    public static range(min: number, max?: number): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return Math.floor(this.random() * (max - min + 1)) + min;
    }

    /**
     * 根据权重数组进行随机，返回结果下标
     * @param weightArr 权重数组
     * @returns 随机到的权重数组下标
     */
    public static randWeightIdx(weightArr: number[]) {
        let sum = 0;
        for (let i = 0; i < weightArr.length; i++) {
            sum += weightArr[i];
        }
        let randNum = this.float(0, sum);
        let curValue = 0
        for (let i = 0; i < weightArr.length; i++) {
            curValue += weightArr[i];
            if (randNum < curValue) {
                return i;
            }
        }
        return weightArr.length - 1;
    }

    /**
     * Fisher–Yates shuffle 数组随机乱序
     */
    public static shuffle<T>(arr: Array<T>): Array<T> {
        for (let i = arr.length - 1; i >= 0; i--) {
            let randomIndex = Math.floor(this.random() * (i + 1));
            [arr[randomIndex], arr[i]] = [arr[i], arr[randomIndex]];
        }
        return arr;
    }

    /**
     * 随机返回数组中的一个元素
     */
    public static arrayRandom<T>(arr: Array<T>): T {
        if (arr.length <= 0) {
            return null!;
        }
        return arr[this.int(0, arr.length)];
    }

    //-----------------------------------------------------------------------------------------------------------------------------------------------------------------

    constructor($seed: string) {
        this._seed = $seed;
        this._seedHash = RandomUtil.MurmurHash3(this._seed);
        this.random = RandomUtil.Mulberry32(this.seedHash);
    }

    private _seedHash: number;
    /**种子hash值 */
    public get seedHash() { return this._seedHash; }

    private _seed: string;
    /**随机种子 */
    public get seed(): string { return this._seed; }

    public static getSeed2(): string {
        return Math.random().toString(36).substring(2);
    }

    public static create(seed?: string): RandomUtil {
        if (!seed) seed = this.getSeed();
        return new RandomUtil(seed);
    }

    public random!: (() => number);

    /**
     * 获取区间[min, max)的整数，传入1个参数则区间为[0, min)
     * [表示大于等于；)表示小于；例如[2,3）表示大于等于2，小于3
     * @param min 
     * @param max 
     * @returns 
     */
    public int(min: number, max?: number): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return Math.floor(this.random() * (max - min)) + min;
    }
    /**
     * 获取区间[min, max]的整数，传入1个参数则区间为[0, min]
     * [表示大于等于；)表示小于；例如[2,3）表示大于等于2，小于3
     * @param min 
     * @param max 
     * @returns 
     */
    public range(min: number, max?: number): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return Math.floor(this.random() * (max - min + 1)) + min;
    }
    /**
     * 获取区间[min, max)的浮点数，传入1个参数则区间为[0, min)
     * [表示大于等于；)表示小于；例如[2,3）表示大于等于2，小于3
     * @param min 
     * @param max 
     * @returns 
     */
    public float(min: number, max?: number): number {
        if (max === undefined) {
            max = min;
            min = 0;
        }
        return this.random() * (max - min) + min;
    }
    /**
     * 随机返回数组中的一个元素
     * @param array 
     * @returns 
     */
    public arrayRandom<T>(array: T[]): T {
        if (array.length <= 0) {
            return null!;
        }
        return array[this.int(array.length)];
    }

    public shuffle<T>(array: T[]): T[] {
        let i = array.length;
        while (--i) {
            let j = Math.floor(this.random() * i);
            [array[i], array[j]] = [array[j], array[i]];
        }
        return array;
    }

    /**
     * 根据权重数组进行随机，返回结果下标
     * @param weightList 权重数组
     * @returns 随机到的权重数组下标
     */
    public randomWeightIdx(weightList: number[]): number {
        let totalWeight = weightList.reduce((a, b) => a + b);
        let randomValue = this.random() * totalWeight;
        let weightIdx = 0;
        for (let i = 0; i < weightList.length; i++) {
            let weight = weightList[i];
            if (randomValue < weight) {
                weightIdx = i;
                break;
            }
            randomValue -= weight;
        }
        return weightIdx;
    }

    /**
     * 在一个数组中随机获取一个元素
     * @param array 数组
     * @returns {any} 随机出来的结果
     */
    private static randomArray<T>(array: Array<T>): T {
        var index: number = Math.floor(Math.random() * array.length);
        return array[index];
    }

    // Define the Murmur3Hash function
    public static MurmurHash3(string: string): number {
        let i = 0;
        var hash: number;
        for (hash = 1779033703 ^ string.length; i < string.length; i++) {
            let bitwise_xor_from_character = hash ^ string.charCodeAt(i);
            hash = Math.imul(bitwise_xor_from_character, 3432918353);
            hash = hash << 13 | hash >>> 19;
        }
        // Return the hash that you can use as a seed
        hash = Math.imul(hash ^ (hash >>> 16), 2246822507);
        hash = Math.imul(hash ^ (hash >>> 13), 3266489909);
        return (hash ^= hash >>> 16) >>> 0;
    }

    /**
     * 使用种子和 SFC32 生成随机数
     * @param seed_1 
     * @param seed_2 
     * @param seed_3 
     * @param seed_4 
     * @returns 
     */
    public static SimpleFastCounter32(seed_1: number, seed_2: number, seed_3: number, seed_4: number) {
        return () => {
            seed_1 >>>= 0; seed_2 >>>= 0; seed_3 >>>= 0; seed_4 >>>= 0;
            let cast32 = (seed_1 + seed_2) | 0;
            seed_1 = seed_2 ^ seed_2 >>> 9;
            seed_2 = seed_3 + (seed_3 << 3) | 0;
            seed_3 = (seed_3 << 21 | seed_3 >>> 11);
            seed_4 = seed_4 + 1 | 0;
            cast32 = cast32 + seed_4 | 0;
            seed_3 = seed_3 + cast32 | 0;
            return (cast32 >>> 0) / 4294967296;
        }
    }

    /**
     * 使用种子和 Mulberry32 生成随机数
     * @param string 
     * @returns 
     */
    public static Mulberry32(string: number) {
        return () => {
            let for_bit32_mul = string += 0x6D2B79F5;
            let cast32_one = for_bit32_mul ^ for_bit32_mul >>> 15;
            let cast32_two = for_bit32_mul | 1;
            for_bit32_mul = Math.imul(cast32_one, cast32_two);
            for_bit32_mul ^= for_bit32_mul + Math.imul(for_bit32_mul ^ for_bit32_mul >>> 7, for_bit32_mul | 61);
            return ((for_bit32_mul ^ for_bit32_mul >>> 14) >>> 0) / 4294967296;
        }
    }

    /**
     * 使用种子和 Xoshiro128** 生成随机数
     * 前两个随机数会是一样的
     * @param seed_1 
     * @param seed_2 
     * @param seed_3 
     * @param seed_4 
     * @returns 
     */
    public static Xoshiro128_twostar(seed_1: number, seed_2: number = 0, seed_3: number = 0, seed_4: number = 0) {
        return () => {
            let t = seed_2 << 9, y = seed_1 * 5; y = (y << 7 | y >>> 25) * 9;
            seed_3 ^= seed_1; seed_4 ^= seed_2;
            seed_2 ^= seed_3; seed_1 ^= seed_4; seed_3 ^= t;
            seed_4 = seed_4 << 11 | seed_4 >>> 21;
            return (y >>> 0) / 4294967296;
        }
    }

    /**
     * 使用种子和 JSF 生成随机数
     * @param seed_1 
     * @param seed_2 
     * @param seed_3 
     * @param seed_4 
     * @returns 
     */
    public static JenkinsSimpleFast32(seed_1: number, seed_2: number, seed_3: number, seed_4: number) {
        return () => {
            seed_1 |= 0; seed_2 |= 0; seed_3 |= 0; seed_4 |= 0;
            let t = seed_1 - (seed_2 << 27 | seed_2 >>> 5) | 0;
            seed_1 = seed_2 ^ (seed_3 << 17 | seed_3 >>> 15);
            seed_2 = seed_3 + seed_4 | 0;
            seed_3 = seed_4 + t | 0;
            seed_4 = seed_1 + t | 0;
            return (seed_4 >>> 0) / 4294967296;
        }
    }

}